/**
 *    @file       bencoding.h
 *
 *    @brief      
 *    @details
 *
 *    @version    1.0
 *    @date       01/25/2014 06:30:18 PM
 *
 *    @author     Theophilus (), wangtf418@gmail.com 
 */
#include "bencoding.h"
#include <cstring>

bf_bc_var_t bc_var_type( const bf_bc_str_t & )
{
	return BF_VT_STR;
}

bf_bc_var_t bc_var_type( const bf_bc_int_t & )
{
	return BF_VT_INT;
}

bf_bc_var_t bc_var_type( const bf_bc_lst_t & )
{
	return BF_VT_LST;
}

bf_bc_var_t bc_var_type( const bf_bc_dic_t & )
{
	return BF_VT_DIC;
}

const bf_char_t *bencoding::dic_prefix	= "{";
const bf_char_t *bencoding::dic_suffix	= "}";
const bf_char_t *bencoding::dic_break	= ";";
const bf_char_t *bencoding::dic_mapping	= "=";
const bf_char_t *bencoding::lst_prefix	= "[";
const bf_char_t *bencoding::lst_suffix	= "]";
const bf_char_t *bencoding::lst_break	= ", ";
const bf_char_t *bencoding::str_prefix	= "'";
const bf_char_t *bencoding::str_suffix	= "'";

bencoding::bencoding()
{
	reset();
}

bencoding::~bencoding()
{
	clear();
}

void bencoding::reset()
{
	m_in_format = ifmt_cod;
	m_out_format = ofmt_des;
	m_type = BF_VT_NULL;
	memset(m_value, 0, sizeof(m_value));
}

void bencoding::clear()
{
	if (m_type != BF_VT_NULL)
	{
		switch(m_type)
		{
		case BF_VT_STR:
			((bf_bc_str_t*)&m_value)->~bf_bc_str_t();
			break;
		case BF_VT_INT:
			((bf_bc_int_t*)&m_value)->~bf_bc_int_t();
			break;
		case BF_VT_LST:
			((bf_bc_lst_t*)&m_value)->~bf_bc_lst_t();
			break;
		case BF_VT_DIC:
			((bf_bc_dic_t*)&m_value)->~bf_bc_dic_t();
			break;
		default:
			break;
		}

		m_type = BF_VT_NULL;
		memset(m_value, 0, sizeof(m_value));
	}
}

bencoding::operator bf_bc_str_t&()
{
	if (m_type != BF_VT_STR)
	{
		clear();
		m_type = BF_VT_STR;
		new ((void *)&m_value)bf_bc_str_t();	//new bf_bc_str_t using memory of m_value
	}
	return *((bf_bc_str_t*)&m_value);
}

bencoding::operator bf_bc_int_t()
{
	if (m_type != BF_VT_INT)
	{
		clear();
		m_type = BF_VT_INT;
		memset(m_value, 0, sizeof(m_value));
	}		
	return *((bf_bc_int_t*)&m_value);
}

bencoding::operator bf_bc_lst_t&()
{
	if (m_type != BF_VT_LST)
	{
		clear();
		m_type = BF_VT_LST;
		new ((void*)&m_value)bf_bc_lst_t();		//new bf_bc_lst_t using memory of m_value
	}		
	return *((bf_bc_lst_t*)&m_value);
}

bencoding::operator bf_bc_dic_t&()
{
	if (m_type != BF_VT_DIC)
	{
		clear();
		m_type = BF_VT_DIC;
		new ((void*)&m_value)bf_bc_dic_t();		//new bf_bc_dic_t using memory of m_value
	}
	return *((bf_bc_dic_t*)&m_value);
}

bf_bc_var_t bencoding::type() const
{
	return m_type;
}

bf_uint_t bencoding::size() const
{
	if (m_type == BF_VT_LST)
	{
		return ((bf_bc_lst_t*)&m_value)->size();
	}
	else if (m_type == BF_VT_DIC)
	{
		return ((bf_bc_dic_t*)&m_value)->size();
	}
	else
	{
		return 0;
	}
}

bencoding & bencoding::operator[](bf_uint_t index)
{
	bf_bc_lst_t &l = *this;
	return l[index];
}

bencoding & bencoding::operator[](const bf_char_t *key)
{
	bf_bc_dic_t &d = *this;
	return d[key];
}

bencoding & bencoding::operator[](const bf_bc_str_t &key)
{
	bf_bc_dic_t &d = *this;
	return d[key];
}

bf_uint_t bencoding::count(const bf_char_t *key)
{
	bf_bc_dic_t &d = *this;
	return d.count(key);
}
bf_uint_t bencoding::count(const bf_bc_str_t &key)
{
	bf_bc_dic_t &d = *this;
	return d.count(key);
}

bencoding & bencoding::format(in_formatm_type io_format)
{
	m_in_format = io_format;
	return *this;
}

bencoding & bencoding::format(out_formatm_type io_format)
{
	m_out_format = io_format;
	return *this;
}

std::ostream & operator<<(std::ostream &out, const bencoding &r)
{
	if (bencoding::ofmt_des == r.m_out_format)
	{
		switch (r.m_type)
		{
		case BF_VT_STR:
			{
				return out << bencoding::str_prefix << *((bf_bc_str_t*)&r.m_value) << bencoding::str_suffix;
				break;
			}
		case BF_VT_INT:
			{
				return out << *((bf_bc_int_t*)&r.m_value);
				break;
			}
		case BF_VT_LST:
			{
				out << bencoding::lst_prefix;
				bf_bc_lst_t & lst = *((bf_bc_lst_t*)&r.m_value);
				bf_bc_lst_t::iterator it = lst.begin();
				for (; it!=lst.end();)
				{
					out << it->format(bencoding::ofmt_des);
					it++;
					if (it==lst.end())
					{
						break;
					}
					out << bencoding::lst_break;
				}
				out << bencoding::lst_suffix;
				break;
			}
		case BF_VT_DIC:
			{
				out << bencoding::dic_prefix;
				bf_bc_dic_t & dic = *((bf_bc_dic_t*)&r.m_value);
				bf_bc_dic_t::iterator it = dic.begin();
				for (; it!=dic.end();)
				{
					out << it->first;
					out << bencoding::dic_mapping;
					out << it->second.format(bencoding::ofmt_des);
					it++;
					if (it==dic.end())
					{
						break;
					}
					out << bencoding::dic_break;
				}
				out << bencoding::dic_suffix;
				break;
			}
		default:
			{
				break;
			}
		}
	}
	else if (bencoding::ofmt_cod == r.m_out_format)
	{
		if (r.m_type == BF_VT_STR)
		{
			bf_bc_str_t &s = *((bf_bc_str_t*)&r.m_value);
			out << s.size();
			out << ':';
			out.write(s.c_str(), s.size());
		}
		else if (r.m_type == BF_VT_INT)
		{
			out << 'i';
			out << *((bf_bc_int_t*)&r.m_value);
			out << 'e';
		}
		else if (r.m_type == BF_VT_LST)
		{
			out << 'l';
			bf_bc_lst_t &l = *((bf_bc_lst_t*)&r.m_value);
			bf_bc_lst_t::iterator it = l.begin();
			for (; it!=l.end(); it++)
			{
				out << (*it).format(bencoding::ofmt_cod);
			}
			out << 'e';
		}
		else if (r.m_type == BF_VT_DIC)
		{
			out << 'd';
			bf_bc_dic_t &d = *((bf_bc_dic_t*)&r.m_value);
			bf_bc_dic_t::iterator it = d.begin();
			for (; it!=d.end(); it++)
			{
				const bf_bc_str_t &s = it->first;
				out << s.size();
				out << ':';
				out.write(s.c_str(), s.size());
				
				out << it->second.format(bencoding::ofmt_cod);
			}
			out << 'e';
		}
		else
		{
			//output nothing : have no chance to enter this branch
		}
	}
	else
	{
		//have no chance to enter this branch
	}
	
	return out;
}	

std::istream & operator>>(std::istream &in, bencoding &r)
{
	r.clear();
	
	if (!in.good())
	{
		return in;	//in stream is not good.
	}
	
	if (bencoding::ifmt_cod == r.m_in_format)
	{
		bf_char_t c;
		if ((in >> c).fail())
		{
			return in;
		}
		
		if (c >= '0' && c <= '9')	//read string : it's integer, so it's a string
		{
			in.unget();
			int i;
			if ((in >> i).fail())
			{
				return in;
			}
			
			if ((in >> c).fail())
			{
				return in;
			}
			
			if (c != ':')
			{
				in.setstate(std::istream::failbit);
				return in;
			}
			
			std::string s(i, ' ');
			if (in.read((bf_char_t*)s.c_str(), i).fail())
			{
				return in;
			}

			r = s;
		}
		else if (c == 'i')		//read integer
		{
			r = 0;
			bf_bc_int_t i = 0;
			if ((in >> i).fail())
			{
				return in;
			}
			if ((in >> c).fail())
			{
				return in;
			}
			if (c != 'e')
			{
				in.setstate(std::istream::failbit);
				return in;
			}
			r = i;
		}
		else if (c == 'l')		//read list
		{
			bf_bc_lst_t &l = r;
			do
			{
				if ((in >> c).fail())	//"le" means an empty list
				{
					return in;
				}
				if (c == 'e')			//end of list
				{
					break;
				}
				in.unget();

				bencoding v;
				if ((in >> v.format(bencoding::ifmt_cod)).fail())
				{
					return in;
				}
				l.push_back(v);
				
				if ((in >> c).fail())
				{
					return in;
				}
				if (c == 'e')			//end of list
				{
					break;
				}
				in.unget();
			}
			while (true);
		}
		else if (c == 'd')
		{
			bf_bc_dic_t &d = r;
			do
			{
				if ((in >> c).fail())	//"de" means an empty dictionary
				{
					return in;
				}
				if (c == 'e')
				{
					break;
				}
				in.unget();

				//read string key
				int i;
				if ((in >> i).fail())
				{
					return in;
				}
				if ((in >> c).fail())
				{
					return in;
				}
				if (c != ':')
				{
					in.setstate(std::istream::failbit);
					return in;
				}
				std::string key(i, ' ');
				if (in.read((bf_char_t*)key.c_str(), i).fail())
				{
					return in;
				}
				
				bencoding v;
				if ((in >> v.format(bencoding::ifmt_cod)).fail())
				{
					return in;
				}
				d.insert(std::pair<std::string, bencoding>(key,v));
				
				if ((in >> c).fail())
				{
					return in;
				}
				if (c == 'e')
				{
					break;
				}
				in.unget();
			}
			while (true);
		}
		else
		{
			in.setstate(std::istream::failbit);		//set in stream state is fail
		}
	}
	else
	{
		in.setstate(std::istream::failbit);			//set in stream state is fail
	}
	
	return in;
}

